package com.minimalist.tc.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.minimalist.common.config.exception.BusinessException;
import com.minimalist.common.config.mybatis.bo.PageResp;
import com.minimalist.common.entity.tc.entity.enums.TcPlateNumberEnum;
import com.minimalist.common.entity.tc.entity.po.TcPlateNumber;
import com.minimalist.common.entity.tc.entity.vo.plateNumber.TcPlateNumberQueryVO;
import com.minimalist.common.entity.tc.entity.vo.plateNumber.TcPlateNumberVO;
import com.minimalist.common.utils.Base64Util;
import com.minimalist.common.utils.FileUtil;
import com.minimalist.common.utils.HttpUtil;
import com.minimalist.common.utils.UnqIdUtil;
import com.minimalist.tc.manager.TcManager;
import com.minimalist.tc.mapper.TcPlateNumberMapper;
import com.minimalist.tc.service.BaiduApiService;
import com.minimalist.tc.service.TcPlateNumberService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

/**
 * 停车车牌表 服务层实现。
 *
 * @author 11987
 * @since 2025-04-07
 */
@Service
public class TcPlateNumberServiceImpl extends ServiceImpl<TcPlateNumberMapper, TcPlateNumber>
        implements TcPlateNumberService{

    private static final Logger log = LoggerFactory.getLogger(TcPlateNumberServiceImpl.class);

    @Autowired
    private TcPlateNumberMapper mapper;

    @Autowired
    private TcManager manager;

    @Autowired
    private BaiduApiService baiduApiService;

    // 图片保存路径
    private static final String IMAGE_SAVE_PATH = "D:\\projects\\zhixin\\minimalist-saas\\images\\plateNumbers";

    @Override
    public void addPlateNumber(TcPlateNumberVO plateNumberVO){
        // 现有代码保持不变
        TcPlateNumber plateNumber = mapper.selectPlateNumberByPlateNumberName(
                plateNumberVO.getPlateNumberName());
        Assert.notNull(plateNumber, () -> new BusinessException(
                TcPlateNumberEnum.ErrorMsgEnum.PLATE_NUMBER_EXIST.getDesc()));
        BeanUtil.copyProperties(plateNumber, plateNumberVO);
        plateNumber.setPlateNumberId(UnqIdUtil.uniqueId());
        mapper.insert(plateNumber);
    }

    @Override
    public void deletePlateNumberByPlateNumberId(Long plateNumberId){
        TcPlateNumber plateNumber = mapper.selectPlateNumberByPlateNumberId(plateNumberId);
        Assert.notNull(plateNumber, () -> new BusinessException(
                TcPlateNumberEnum.ErrorMsgEnum.PLATE_NUMBER_NOT_EXIST.getDesc()));
        mapper.deletePlateNumberByPlateNumberId(plateNumberId);
    }

    @Override
    public void updatePlateNumberByPlateNumberId(TcPlateNumberVO plateNumberVO){
        TcPlateNumber plateNumber = mapper.selectPlateNumberByPlateNumberId(
                plateNumberVO.getPlateNumberId());
        Assert.notNull(plateNumber, () -> new BusinessException(
                TcPlateNumberEnum.ErrorMsgEnum.PLATE_NUMBER_NOT_EXIST.getDesc()));
        BeanUtil.copyProperties(plateNumber, plateNumberVO);
        mapper.updatePlateNumberByPlateNumberId(plateNumber);
    }

    @Override
    public TcPlateNumberVO getPlateNumberByPlateNumberId(Long plateNumberId){
        TcPlateNumber plateNumber = mapper.selectPlateNumberByPlateNumberId(plateNumberId);
        Assert.notNull(plateNumber, () -> new BusinessException(
                TcPlateNumberEnum.ErrorMsgEnum.PLATE_NUMBER_NOT_EXIST.getDesc()));
        TcPlateNumberVO plateNumberVO = new TcPlateNumberVO();
        BeanUtil.copyProperties(plateNumber, plateNumberVO);
        return plateNumberVO;
    }

    @Override
    public PageResp<TcPlateNumberVO> getPagePlateNumberList(TcPlateNumberQueryVO queryVO){
        Page<TcPlateNumber> page = mapper.selectPagePlateNumberList(queryVO);
        if (page.hasRecords()){
            List<TcPlateNumberVO> list = BeanUtil.copyToList(page.getRecords(),
                    TcPlateNumberVO.class);
            return new PageResp<>(list, page.getTotalRow());
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertPlateNumber(Integer count){
        int result = manager.insertPlateNumbersFromOrder(count);
        Assert.isTrue(count == result,
                () -> new BusinessException(TcPlateNumberEnum.ErrorMsgEnum.PLATE_NUMBER_INSERT_ERROR.getDesc()));
    }

    /**
     * 从识别结果中提取车辆品牌信息
     *
     * @param identifyResult 识别结果JSON字符串
     * @return 车辆品牌，如果解析失败则返回null
     */
    private String extractBrandFromResult(String identifyResult){
        if (StrUtil.isBlank(identifyResult)){
            return null;
        }

        try{
            // 使用FastJson解析JSON
            JSONObject jsonObject = JSON.parseObject(identifyResult);
            // 直接获取brand字段
            if (jsonObject.containsKey("brand")){
                return jsonObject.getString("brand");
            }
            return null;
        } catch (Exception e){
            log.error("解析识别结果失败", e);
            return null;
        }
    }

    /**
     * 根据fileId查找最佳图片（分辨率最大的图片）
     *
     * @param fileId 文件ID
     * @return 最佳图片的完整路径
     */
    private String findBestImageByFileId(Long fileId){
        if (fileId == null){
            return null;
        }

        // 构建文件夹路径
        String folderPath = IMAGE_SAVE_PATH + File.separator + fileId;
        File folder = new File(folderPath);

        // 检查文件夹是否存在
        if (!folder.exists() || !folder.isDirectory()){
            log.warn("文件夹不存在：{}", folderPath);
            return null;
        }

        // 获取文件夹下所有图片文件
        File[] imageFiles = folder.listFiles(file -> {
            String name = file.getName().toLowerCase();
            return file.isFile() && (name.endsWith(".jpg") || name.endsWith(".jpeg")
                    || name.endsWith(".png") || name.endsWith(".bmp"));
        });

        if (imageFiles == null || imageFiles.length == 0){
            log.warn("文件夹中没有图片文件：{}", folderPath);
            return null;
        }

        // 查找分辨率最大的图片
        Optional<File> bestImage = Arrays.stream(imageFiles)
                .map(file -> {
                    try{
                        BufferedImage image = ImageIO.read(file);
                        if (image != null){
                            // 计算图片像素数（分辨率）
                            long pixels = (long) image.getWidth() * image.getHeight();
                            return new Object[]{file, pixels};
                        }
                    } catch (IOException e){
                        log.warn("读取图片失败：{}", file.getAbsolutePath(), e);
                    }
                    return new Object[]{file, 0L};
                })
                .filter(obj -> (Long) obj[1] > 0)
                .max(Comparator.comparingLong(obj -> (Long) obj[1]))
                .map(obj -> (File) obj[0]);

        return bestImage.map(File::getAbsolutePath).orElse(null);
    }

    private String recognizeCar(String filePath){
        // 请求url
        String url = "https://aip.baidubce.com/rest/2.0/image-classify/v1/car";
        try{
            byte[] imgData = FileUtil.readFileByBytes(filePath);
            String imgStr = Base64Util.encode(imgData);
            String imgParam = URLEncoder.encode(imgStr, StandardCharsets.UTF_8);

            String param = "image=" + imgParam + "&top_num=" + 5 + "&output_brand=true";

            // 注意这里仅为了简化编码每一次请求都去获取access_token，线上环境access_token有过期时间， 客户端可自行缓存，过期后重新获取。
            String accessToken = baiduApiService.getAccessToken();

            String result = HttpUtil.post(url, accessToken, param);
            System.out.println(result);
            return result;
        } catch (Exception e){
            log.error("识别车牌失败", e);
            throw new BusinessException("识别车牌失败：" + e.getMessage());
        }
    }

    /**
     * 识别有文件ID的车牌
     *
     * @param count 识别数量
     * @return 成功识别的数量
     */
    @Override
    public int recognizePlateNumberWithFileId(Integer count){
        List<TcPlateNumber> plateNumbers = mapper.selectNotRecognizePlateNumbersWithFileId(count);
        if (plateNumbers == null || plateNumbers.isEmpty()){
            log.info("没有需要识别的车牌图片或所有车牌都没有关联文件ID");
            return 0;
        }

        log.info("开始识别车牌，数量：{}", plateNumbers.size());

        int successCount = 0; // 成功识别的车牌数量

        for (TcPlateNumber plateNumber : plateNumbers){
            try{
                // 获取fileId (这里已经确保fileId不为空)
                Long fileId = plateNumber.getFileId();

                // 查找最佳图片
                String bestImagePath = findBestImageByFileId(fileId);
                if (StrUtil.isBlank(bestImagePath)){
                    log.warn("车牌ID：{}，文件ID：{}，未找到有效图片", plateNumber.getPlateNumberId(), fileId);
                    continue;
                }

                log.info("车牌ID：{}，使用图片：{} 进行识别", plateNumber.getPlateNumberId(), bestImagePath);

                // 调用百度API识别车牌
                String recognizeResult = recognizeCar(bestImagePath);

                // 提取品牌信息
                String brand = extractBrandFromResult(recognizeResult);
                if (StrUtil.isNotBlank(brand)){
                    // 将品牌信息设置到车型字段
                    plateNumber.setVehicleModel(brand);
                    log.info("车牌ID：{}，识别到车辆品牌：{}", plateNumber.getPlateNumberId(), brand);
                }

                // 更新识别结果
                plateNumber.setIdentifyResult(recognizeResult);
                boolean updated = this.updateById(plateNumber);

                if (updated){
                    log.info("车牌ID：{}，识别完成", plateNumber.getPlateNumberId());
                    successCount++; // 成功识别计数加1
                } else{
                    log.warn("车牌ID：{}，更新失败", plateNumber.getPlateNumberId());
                }
            } catch (Exception e){
                log.error("识别车牌出错，车牌ID：{}", plateNumber.getPlateNumberId(), e);
            }
        }

        log.info("车牌识别完成，总数：{}，成功数：{}", plateNumbers.size(), successCount);
        return successCount;
    }

}
